library ieee;

use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

use work.cpu_utils.all;
use work.alu_utils.all;


entity ALU is
	generic (
		size: integer := 32;
		Tpd : Time := unit_delay
	);
	port (
		operand1 : in bit_vector(size-1 downto 0);
		operand2 : in bit_vector(size-1 downto 0);
		result : out bit_vector(size-1 downto 0);
		condition_flag : out bit_vector(2 downto 0);
		command : in ALU_command
	);
end ALU;


architecture alu_arh of ALU is
	alias flag_N : bit is condition_flag(2);
	alias flag_Z : bit is condition_flag(1);
	alias flag_V : bit is condition_flag(0);
begin
	ALU_function: process (operand1, operand2, command)
		variable a: integer;
		variable temp_result : signed(size-1 downto 0);--bit_vector(size-1 downto 0);
		variable bit_n, bit_z, bit_v: bit := '0';
		
	begin
	
	
		case command is
			when alu_nop =>
				temp_result := (others=>'0');--temp_result := to_signed(operand1);--menjaj!!!!!!
			when alu_add =>
				temp_result := to_signed(operand1) + to_signed(operand2);
			when alu_sub =>
				temp_result := to_signed(operand1) - to_signed(operand2);
			when alu_and =>
				temp_result := to_signed(operand1) and to_signed(operand2);
			when alu_or =>
				temp_result := to_signed(operand1) or to_signed(operand2);
			when alu_xor =>
				temp_result := to_signed(operand1) xor to_signed(operand2);
			when alu_not =>
				temp_result := not to_signed(operand1);
			when alu_shl =>
				temp_result := to_signed(operand1) sll to_integer(operand2);
			when alu_shr =>
				temp_result := to_signed(to_integer (to_unsigned(operand1) srl to_integer(operand2)), size);
			when alu_sar =>
				temp_result := to_signed(operand1) srl to_integer(operand2);
			when alu_rol =>
				temp_result := to_signed(operand1) rol to_integer(operand2);
			when alu_ror =>
				temp_result := to_signed(operand1) ror to_integer(operand2);
			when alu_transA =>
				temp_result := to_signed(operand1);
			when alu_transB =>
				temp_result := to_signed(operand2);
			when alu_inc =>
				temp_result := to_signed(operand1) + 1;
		end case;
		
				
		
		result <= to_bits(temp_result(size-1 downto 0), size) after Tpd;
		
		case command is
			when alu_add =>
				if operand1(size-1)='1' and operand2(size-1)='1' and temp_result(size-1)='0' 
					then bit_V := '1';
				elsif operand1(size-1)='0' and operand2(size-1)='0' and temp_result(size-1)='1'
					then bit_V := '1';
				else bit_V := '0';
				end if;
			when alu_sub =>
				if operand1(size-1)='0' and operand2(size-1)='1' and temp_result(size-1)='1' 
					then bit_V := '1';
				elsif operand1(size-1)='1' and operand2(size-1)='0' and temp_result(size-1)='0'
					then bit_V := '1';
				else bit_V := '0';
				end if;
			when others =>
				bit_V := '0';
		end case;

		case command is
			when alu_nop => 
				bit_N := '0';
				bit_Z := '1';
				bit_V := '0';
			when others =>
					bit_N := to_bits(temp_result, size)(size-1);
					bit_Z := to_bits(temp_result, size)(0);
					for i in 1 to size-1 loop
						bit_Z := bit_Z or to_bits(temp_result, size)(i);
					end loop;
					bit_Z := not bit_Z;
		end case;
		
		--bit_C := 
		
		flag_N <= bit_N after Tpd;
		flag_Z <= bit_Z after Tpd;
		flag_V <= bit_V after Tpd;
		
	end process ALU_function;
	
end alu_arh;